package com.shigen.demo.util;

import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class IPUtil {
    public static String getIp(HttpServletRequest request) {
        // 获取X-Forwarded-For
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // 获取Proxy-Client-IP
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // WL-Proxy-Client-IP
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            // 获取的IP实际上是代理服务器的地址，并不是客户端的IP地址
            ip = request.getRemoteAddr();
            if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
                // 根据网卡取本机配置的IP
                try {
                    ip = InetAddress.getLocalHost().getHostAddress();
                } catch (UnknownHostException e) {
                    log.error("get local host exception", e.getMessage());
                }
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15) { // "***.***.***.***".length()
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }

    public static String localIp() {
        String localIp = null;
        try {
            localIp = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("get local host exception", e.getMessage());
        }
        return localIp;
    }

    public static boolean isIp(String ip) {
        if (ip.length() < 7 || ip.length() > 15) {
            return false;
        }
        String regex = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
        Pattern pat = Pattern.compile(regex);
        Matcher mat = pat.matcher(ip);
        return mat.find();
    }


    public static String getCityInfo(String ip) throws IOException {
        if (!Util.isIpAddress(ip)) {
            System.out.println("Error: Invalid ip address");
        }

        // 获取ip库路径
        ClassPathResource classPathResource = new ClassPathResource("ip2region.db");
        if (classPathResource.getClassLoader() == null) {
            log.error("存储路径发生错误，没有被发现");
            return null;
        }
        InputStream inputStream = classPathResource.getInputStream();
        byte[] bytes = IoUtil.readBytes(inputStream);

        // 查询算法：B-tree、Binary、Memory
        int algorithm = DbSearcher.MEMORY_ALGORITYM;
        try {
            DbSearcher searcher = new DbSearcher(new DbConfig(), bytes);

            String value = AlgorithmEnum.getMethodByAlgorithm(algorithm);
            if (StringUtils.isEmpty(value)) {
                return null;
            }

            Method method = searcher.getClass().getMethod(value, String.class);
            DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
            return dataBlock.getRegion();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public enum AlgorithmEnum {
        /**
         * 查询算法
         */
        B_TREE_SEARCH(DbSearcher.BTREE_ALGORITHM, "btreeSearch"),
        BINARY_SEARCH(DbSearcher.BINARY_ALGORITHM, "binarySearch"),
        MEMORY_SEARCH(DbSearcher.MEMORY_ALGORITYM, "memorySearch");

        /**
         * 算法类型
         **/
        private final int algorithm;
        /**
         * 使用算法名称
         **/
        private final String value;

        public static String getMethodByAlgorithm(int algorithm) {
            for (AlgorithmEnum algorithmEnum : AlgorithmEnum.values()) {
                if (algorithmEnum.algorithm == algorithm) {
                    return algorithmEnum.value;
                }
            }
            return null;
        }

        AlgorithmEnum(int algorithm, String value) {
            this.algorithm = algorithm;
            this.value = value;
        }
    }
}
